home *** CD-ROM | disk | FTP | other *** search
- Path: uunet!tektronix!tekgen!tekred!games
- From: games@tekred.CNA.TEK.COM
- Newsgroups: comp.sources.games
- Subject: v06i026: puzzle15-2 - revised puzzle15 (15 square puzzle)
- Message-ID: <3721@tekred.CNA.TEK.COM>
- Date: 15 Mar 89 22:36:07 GMT
- Sender: billr@tekred.CNA.TEK.COM
- Lines: 1163
- Approved: billr@saab.CNA.TEK.COM
-
- Submitted-by: "P. Knoppers" <nluug.nl!duteca!knop@tektronix>
- Posting-number: Volume 6, Issue 26
- Archive-name: puzzle15-2
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: README Makefile puzzle15.6 puzzle15.c
- # Wrapped by billr@saab on Wed Mar 15 14:36:49 1989
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(604 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- XI have applied some of the suggestions put to me by various persons
- X(acknowledged in the program text). The program can be compiled with
- Xthe option -DARROWKEYS, but I am not sure that it works - I cannot
- Xtest it. The other compile-time options have been tested and they work.
- X
- XIn the following shell archive you will find the source of the program
- Xand a manual page in [nt]roff -man format.
- X
- XSince the previous version I have moved to another machine. My email
- Xaddress is now knop@duteca.UUCP. Mail to the previous machine may not
- Xbounce, in which case it might appear in my mailbox on the new machine.
- X
- END_OF_FILE
- if test 604 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(101 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- X# simple makefile for puzzle15
- Xpuzzle15: puzzle15.c
- X cc -o puzzle15 -O puzzle15.c -lcurses -ltermcap
- END_OF_FILE
- if test 101 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test -f 'puzzle15.6' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'puzzle15.6'\"
- else
- echo shar: Extracting \"'puzzle15.6'\" \(6379 characters\)
- sed "s/^X//" >'puzzle15.6' <<'END_OF_FILE'
- X.TH PUZZLE15 6
- X.UC 4
- X.SH NAME
- Xpuzzle15 \- simulate a well known toy with many variations
- X.SH SYNOPSIS
- X.br
- X.B puzzle15
- X[<width[x<height>]]
- X[movemethod]
- X[cursormethod]
- X[facemethod]
- X.SH DESCRIPTION
- X.I Puzzle15
- Xsimulates a game that is available in many toy shops. This computerized
- Xversion is crt-oriented and offers fewer possibilities for cheating.
- X.br
- XWhen invoked without arguments
- X.I puzzle15
- Xpresents a 4 by 4 grid with 15 numbered tiles and one empty square.
- XThe tiles are not ordered in sequence.
- XThe tiles adjacent to the empty square can exchange places with the
- Xempty square. This is done by indicating the direction that the empty
- Xsquare is to move with the vi(1) cursor keys (k for up, j for down,
- Xh for left and l for right).
- X.br
- XThe intention of the game is to rearrange the tiles in their ``natural''
- Xsequence.
- X.PP
- XThe program keeps track of the time and the number of moves used.
- XThis makes it possible to arrange competitions where time, or the
- Xnumber of moves used determines the winner.
- X.PP
- XThe program ends when the tiles are arranged in sequence, or when you
- Xtype your interrupt character (usually ctrl-C or break). On computers
- Xthat lack good support for keyboard interrupts a key can be selected
- Xthat will terminate the program. (This is a compile-time option.)
- X.SH VARIATIONS
- XThe look and feel of the program can be altered by specifying different
- Xdimensions for the array, the way that the tile to move is selected
- X(movemethod), the keys that specify where the cursor is to go
- X(cursormethod) and the faces that are shown on the tiles (facemethod).
- X.SH DIMENSIONS
- XThe dimensions of the board can be varied within the limits of your
- Xterminal screen. To obtain a square array a single numeric argument
- Xis required.
- X.I Puzzle15
- X.I 3
- Xpresents a 3x3 array with 8 numbered tiles. With numbered tiles it
- Xis not possible to extend the array beyond 10 by 10, because only two
- Xdigits are used to identify each tile.
- X.PP
- XTo obtain a non-square array an argument that consists of a number,
- Xthe character 'x' and another number must be supplied.
- X.I Puzzle15
- X.I 6x3
- Xpresents a grid that is 6 columns wide and 3 rows high with 17
- Xnumbered tiles.
- X.SH MOVEMETHODS
- XThe default movemethod is to indicate the direction that the empty
- X(blank) field is to move. This is called
- X.I moveblank.
- XThere are two other methods that can be selected by an argument on the
- Xcommand line.
- X.br
- X.I Movetile
- Xtells
- X.I puzzle15
- Xthat you want to indicate the direction that an adjacent tile is to be
- Xmoved. This is equivalent to reversal of the directions of the cursor
- Xkeys.
- X.br
- X.I Selecttile
- Xtells
- X.I puzzle15
- Xthat you want to indicate the new position of the empty field by
- Xmoving the cursor to that position and then typing a <space>, or
- X<return> (or whatever you like). In this way you can move several
- Xtiles in one blow.
- X.SH CURSORMETHODS
- XThe default way to move the cursor or the empty field is to use the
- Xvi(1) cursor keys.
- X.br
- XOn some computers it is possible to specify
- X.I arrowkeys,
- Xwhich tells
- X.I puzzle15
- Xto use the arrow keys of your terminal to specify a move.
- X.br
- XIf you specify
- X.I numberkeys
- Xon the command line,
- X.I puzzle15
- Xwill use '8' for up, '2' for down, '4' for left and '6' for right.
- Xthis corresponds to arrows on the numeric key pad of most personal
- Xcomputers.
- X.br
- XAnother way to specify where the empty field is to go is obtained by
- Xspecifying
- X.I facekeys
- Xon the command line. In this mode you move the empty field to a new
- Xposition by typing the character(s) shown on the tile that currently
- Xoccupies that position.
- X.I Facekeys
- Ximplies use of the movemethod
- X.I selecttile.
- XIf the
- X.I facekeys
- Xcursormethod is used with numbered tiles, some tile numbers are shown
- Xwith leading zero. To select such a tile a leading zero is required.
- XTiles that are labeled with a single digit can be selected with that
- Xdigit, or with a leading zero followed by that digit.
- X.br
- XFinally you can specify your own cursor movement keys with
- X.I keys
- X.I <up><down><left><right>
- Xon the command line. <Up> is the key that you will use to move
- Xup, etc.
- X.SH FACEMETHODS
- XThe default way that
- X.I puzzle15
- Xuses to identify the tiles is to number them. If you prefer
- Xtiles that are marked with letters you must specify
- X.I alphafaces
- Xon the command line.
- X.I Puzzle15
- Xwill now use the lower-case letters a..z to identify the tiles.
- X.br
- XSimilarly, if you specify
- X.I numberalphafaces
- X.I puzzle15
- Xwill use the digits 0..9 followed by the lower-case letters a..z to
- Xidentify the tiles.
- X.br
- X.I Alphanumberfaces
- Xselects the lower-case letters a..z followed by the digits 0..9.
- X.br
- XFinally, you can specify the characters to use on the command line with
- X.I faces
- X.I <facecharacters>.
- XThe order in
- X.I <facecharacters>
- Xwill be used to determine when the tiles are ordered.
- X.SH EXAMPLES
- X.I puzzle15
- X.I faces
- X.I '1234567890qwertyuiopasdfghjkl;zxcvbnm,.'
- X.I 10x4
- X.br
- Xcreates a 10 by 4 array filled with the symbols of a typewriter
- Xkeyboard. The position to achieve is the ordering of the keys on
- Xthe typewriter. Quoting is necessary to prevent the shell from
- Xtreating the ``;'' as a special character. By adding the option
- X.I facekeys
- Xthe program turns into a tough typing tutor, especially if you decide
- Xnot to take your eyes off the screen.
- X.SH DIAGNOSTICS
- XAn unknown keyword or an illegal combination of keywords on the command
- Xline generates a complete list of possible keywords and restrictions on
- Xtheir combinations.
- X.br
- XIf you type an undefined key during the game, the program lists the keys
- Xthat are valid.
- X.br
- XOn startup fatal diagnostics from the curses screen handling package
- Xare possible if your terminal is ill-defined.
- X.br
- XIf the program detects an internal error a diagnostic starting with the
- Xletters
- X.I aargh:
- Xis printed. If you manage to get one of these, I like to know how
- Xyou did it.
- X.SH AUTHOR
- XP. Knoppers - knop@duteca.UUCP.
- X.br
- XPaul Lew added arrow keys.
- X.br
- XBo Kullmar added the clock and the move counter.
- X.br
- XLarry Hastings suggested the selecttile method.
- X.SH COPYRIGHT
- XThe
- X.I puzzle15
- Xprogram is protected by copyright (C) 1984, 1988, 1989 by P. Knoppers.
- XDistribution of unmodified copies of the program with source is unrestricted.
- XYou are not allowed to distribute modified copies. This is to prevent
- Xuncontrolled spreading of a zillion different versions.
- XYou can mail suggestions for improvements to me. I
- X.I may
- Xincorporate them in a future version.
- END_OF_FILE
- if test 6379 -ne `wc -c <'puzzle15.6'`; then
- echo shar: \"'puzzle15.6'\" unpacked with wrong size!
- fi
- # end of 'puzzle15.6'
- fi
- if test -f 'puzzle15.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'puzzle15.c'\"
- else
- echo shar: Extracting \"'puzzle15.c'\" \(23213 characters\)
- sed "s/^X//" >'puzzle15.c' <<'END_OF_FILE'
- X/*
- X * This program simulates a simple toy with many many variations.
- X *
- X * Compile with -DARROWKEYS if your curses library understands about
- X * arrow keys (and your keyboards have them).
- X * Compile with -DNOSIGNAL if your machine does not support the signal
- X * function to catch ctrl-C.
- X * Compile with "-DDIECHAR='<somechar>'" if you would like the program
- X * to exit gracefully when <somechar> is typed. All the quotes in and
- X * around this argument are necessary. The double quotes prevent the
- X * shell from stripping the single quotes. Instead of '<somechar' you
- X * can also type the ascii value of the key. If you do so, the double
- X * quotes are not needed.
- X *
- X * The copyright message is there to prevent uncontrolled spreading of
- X * a zillion different versions.
- X *
- X * You can mail your improvements and suggestions to me. I may include
- X * them in a future version of the program.
- X *
- X * I do not guarantee the fitness of this program for any purpose.
- X * USE IT AT YOUR OWN RISK.
- X *
- X * Peter Knoppers - knop@duteca.UUCP
- X * Bilderdijkhof 59
- X * 2624 ZG Delft
- X * The Netherlands
- X *
- X *
- X * The following lines are put in the compiled program by the C compiler
- X * where they can be found by programs such as (Bsd) strings.
- X */
- Xchar *this_is = "puzzle15 V2.0 Feb 24 1989";
- Xchar *author0 = "(C) Copyright 1985, 1988, 1989 Peter Knoppers";
- Xchar *author1 = "Arrow keys added by Paul Lew 1988";
- Xchar *author2 = "Clock added by Bo Kullmar 1988";
- Xchar *author3 = "Select-tile method suggested by Larry Hastings 1988";
- Xchar *authorx = "Additions merged by Peter Knoppers";
- Xchar *release1 = "Permission to use and redistribute unmodified ";
- Xchar *release2 = "copies with source is granted to everyone";
- Xchar *release3 = "Distribution of modified copies is NOT allowed";
- X
- X#include <curses.h>
- X#ifndef NOSIGNAL
- X#include <signal.h>
- X#endif
- X#include <ctype.h>
- X
- X#ifndef DIECHAR
- X#define DIECHAR 999 /* any non-ASCII value will do */
- X#endif
- X#define DEFSIZE 4 /* default size of board */
- X#define BLANK 0
- X#define UNSPECIFIED (-1)
- X/* classification of arguments */
- X#define MOVEMETHOD 1
- X#define CURSORMETHOD 2
- X#define FACEMETHOD 3
- X/* magic values are required to be different, no other restrictions apply */
- X/* magic values for Movemethods */
- X#define MOVEBLANK 4
- X#define MOVETILE 5
- X#define SELECTTILE 6
- X/* magic values for Cursormethods */
- X#define VIKEYS 7
- X#ifdef ARROWKEYS
- X#define ARROWK 8
- X#endif
- X#define NUMBERKEYS 9
- X#define FACEKEYS 10
- X#define KEYS 11
- X/* magic values for Facemethods */
- X#define NUMBERFACES 12
- X#define ALPHAFACES 13
- X#define NUMBERALPHAFACES 14
- X#define ALPHANUMBERFACES 15
- X#define FACES 16
- X
- Xunsigned long Time_start;
- Xint Movecnt = 0;
- X
- Xchar *malloc (); /* to satisfy lint */
- Xlong time ();
- X
- Xint Sizex = UNSPECIFIED; /* x-size of the board */
- Xint Sizey = UNSPECIFIED; /* y-size of the board */
- Xint Size2; /* total surface of the board */
- Xint Movemethod = UNSPECIFIED;
- Xint Cursormethod = UNSPECIFIED;
- Xint Facemethod = UNSPECIFIED;
- Xchar *Movename; /* name of the selected Movemethod */
- Xchar *Cursorname; /* name of the selected Cursormethod */
- Xchar *Cursorkeys; /* the user-defined Cursorkeys */
- Xchar *Facename; /* name of the selected Facemethod */
- Xchar *Facechars; /* the user-defined tile-faces */
- Xint Up; /* key-codes for cursor movements */
- Xint Down;
- Xint Left;
- Xint Right;
- Xint Newpos; /* new position for empty field */
- Xchar *Myname; /* name used to invoke this program */
- X/*
- X * All possible keyword arguments are listed in the Argopts table.
- X * This is done in order to
- X * - ensure that every keyword appears at only one place in the program
- X * (this makes the program easier to modify/maintain)
- X * - put most of the knowledge about incompatible arguments in one place
- X * instead of scattering it all over the program.
- X * - simplify the argument parser
- X * (I haven't been completely succesfull in this respect...)
- X */
- Xstruct argtablemember
- X{
- X char *a_name; /* what the user types */
- X int a_type; /* what kind of spec. it is */
- X int a_magic; /* magic value */
- X} Argopts[] =
- X{
- X {
- X "moveblank", MOVEMETHOD, MOVEBLANK
- X },
- X {
- X "movetile", MOVEMETHOD, MOVETILE
- X },
- X { /* SELECTTILE must be before FACEKEYS */
- X "selecttile", MOVEMETHOD, SELECTTILE
- X },
- X {
- X "vikeys", CURSORMETHOD, VIKEYS
- X },
- X#ifdef ARROWKEYS
- X {
- X "arrowkeys", CURSORMETHOD, ARROWK
- X },
- X#endif
- X {
- X "numberkeys", CURSORMETHOD, NUMBERKEYS
- X },
- X {
- X "facekeys", CURSORMETHOD, FACEKEYS
- X },
- X {
- X "keys", CURSORMETHOD, KEYS
- X },
- X {
- X "numberfaces", FACEMETHOD, NUMBERFACES
- X },
- X {
- X "alphafaces", FACEMETHOD, ALPHAFACES
- X },
- X {
- X "numberalphafaces", FACEMETHOD, NUMBERALPHAFACES
- X },
- X {
- X "alphanumberfaces", FACEMETHOD, ALPHANUMBERFACES
- X },
- X {
- X "faces", FACEMETHOD, FACES
- X },
- X {
- X (char *) 0, 0, 0
- X }
- X};
- X
- Xdie () /* nice exit on ctrl-C */
- X{
- X#ifndef NOSIGNAL
- X signal (SIGINT, SIG_IGN); /* ignore ctrl-C */
- X#endif
- X mvprintw (LINES - 1, 0, "Goodbye. ");
- X clrtoeol ();
- X refresh ();
- X endwin (); /* shutdown curses, restore ttymode */
- X exit (0);
- X}
- X
- Xmain (argc, argv) /* scan the arguments, call game() */
- Xchar **argv;
- X{
- X struct argtablemember *atmp;/* to walk Argopts table */
- X int swap; /* to revers directions */
- X
- X Myname = *argv; /* save this for usage () */
- X /*
- X * scan the arguments
- X */
- X while (*++argv != ((char *) 0))
- X { /* walk argument list */
- X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
- X { /* scan the keyword list */
- X if (strcmp (*argv, atmp -> a_name) == 0)
- X { /* found keyword */
- X switch (atmp -> a_type)
- X {
- X case MOVEMETHOD:
- X if (Movemethod != UNSPECIFIED)
- X conflict (Movename, atmp -> a_name);
- X Movename = atmp -> a_name;
- X Movemethod = atmp -> a_magic;
- X break;
- X case CURSORMETHOD:
- X if (Cursormethod != UNSPECIFIED)
- X conflict (Cursorname, atmp -> a_name);
- X Cursorname = atmp -> a_name;
- X Cursormethod = atmp -> a_magic;
- X if (atmp -> a_magic == KEYS)
- X {
- X if ((Cursorkeys = *++argv) == (char *) 0)
- X novalue (*--argv);/* never returns */
- X if (strlen (Cursorkeys) != 4)
- X {
- X printf ("%s need 4 cursorkeys\n",
- X Cursorname);
- X usage ();
- X }
- X }
- X break;
- X case FACEMETHOD:
- X if (Facemethod != UNSPECIFIED)
- X conflict (Facename, atmp -> a_name);
- X Facename = atmp -> a_name;
- X Facemethod = atmp -> a_magic;
- X if (atmp -> a_magic == FACES)
- X if ((Facechars = *++argv) == (char *) 0)
- X novalue (*--argv);
- X break;
- X default:
- X printf ("aargh: bad switch (atmp -> a_type = %d)\n",
- X atmp -> a_type);
- X exit (1);
- X }
- X break;
- X }
- X }
- X if (atmp -> a_name == (char *) 0)/* not a keyword */
- X if (isdigit (**argv) && (Sizex == UNSPECIFIED))
- X { /* it's a boardsize specification */
- X if (sscanf (*argv, "%dx%d", &Sizex, &Sizey) != 2)
- X if (sscanf (*argv, "%d", &Sizex) == 1)
- X Sizey = Sizex;
- X else
- X {
- X printf ("bad argument %s\n", *argv);
- X usage ();
- X }
- X }
- X else /* it's garbage */
- X {
- X printf ("bad argument %s\n", *argv);
- X usage ();
- X }
- X }
- X
- X /*
- X * insert default values
- X */
- X if (Sizex == UNSPECIFIED)
- X Sizex = Sizey = DEFSIZE;
- X
- X if (Cursormethod == UNSPECIFIED)
- X { /* find first CURSORMETHOD in Argopts */
- X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
- X if (atmp -> a_type == CURSORMETHOD)
- X break;
- X if (atmp -> a_name == (char *) 0)
- X {
- X printf ("aargh: can't find default Cursormethod\n");
- X exit (1);
- X }
- X Cursormethod = atmp -> a_magic;
- X Cursorname = atmp -> a_name;
- X }
- X
- X if (Facemethod == UNSPECIFIED)
- X { /* find first FACEMETHOD in Argopts */
- X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
- X if (atmp -> a_type == FACEMETHOD)
- X break;
- X if (atmp -> a_name == (char *) 0)
- X {
- X printf ("aargh: can't find default Facemethod\n");
- X exit (1);
- X }
- X Facemethod = atmp -> a_magic;
- X Facename = atmp -> a_name;
- X }
- X
- X if (Movemethod == UNSPECIFIED)
- X if (Cursormethod == FACEKEYS)
- X Movemethod = SELECTTILE;/* by implication */
- X else
- X { /* find first MOVEMETHOD in Argopts */
- X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
- X if (atmp -> a_type == MOVEMETHOD)
- X break;
- X if (atmp -> a_name == (char *) 0)
- X {
- X printf ("aargh: can't find default Movemethod\n");
- X exit (1);
- X }
- X Movemethod = atmp -> a_magic;
- X Movename = atmp -> a_name;
- X }
- X
- X if ((Cursormethod == FACEKEYS) && (Movemethod != SELECTTILE))
- X conflict (Cursorname, Movename);/* does not return */
- X switch (Cursormethod)
- X {
- X case VIKEYS:
- X Up = 'k';
- X Down = 'j';
- X Left = 'h';
- X Right = 'l';
- X break;
- X#ifdef ARROWKEYS
- X case ARROWK:
- X Up = KEY_UP;
- X Down = KEY_DOWN;
- X Left = KEY_LEFT;
- X Right = KEY_RIGHT;
- X break;
- X#endif
- X case NUMBERKEYS:
- X Up = '8';
- X Down = '2';
- X Left = '4';
- X Right = '6';
- X break;
- X case FACEKEYS:
- X Up = 1000; /* values must not correspond to any ASCII */
- X Down = 1001; /* value, otherwise no restrictions */
- X Left = 1002;
- X Right = 1003;
- X break;
- X case KEYS:
- X Up = Cursorkeys[0];
- X Down = Cursorkeys[1];
- X Left = Cursorkeys[2];
- X Right = Cursorkeys[3];
- X break;
- X default:
- X printf ("aargh: bad switch (Cursormethod = %d)\n",
- X Cursormethod);
- X exit (1);
- X }
- X
- X if (Movemethod == MOVETILE)
- X { /* revers cursor directions */
- X swap = Up;
- X Up = Down;
- X Down = swap;
- X swap = Left;
- X Left = Right;
- X Right = swap;
- X }
- X
- X Size2 = Sizex * Sizey; /* surface area of the board */
- X
- X switch (Facemethod)
- X {
- X case NUMBERFACES:
- X if (Size2 <= 10) /* tiles can be labeled with 1 char */
- X {
- X Facechars = "123456789";
- X Facemethod = NUMBERALPHAFACES;
- X }
- X break;
- X case ALPHAFACES:
- X Facechars = "abcdefghijklmnopqrstuvwxyz";
- X break;
- X case NUMBERALPHAFACES:
- X Facechars = "0123456789abcdefghijklmnopqrstuvwxyz";
- X break;
- X case ALPHANUMBERFACES:
- X Facechars = "abcdefghijklmnopqrstuvwxyz0123456789";
- X break;
- X }
- X
- X if ((Size2 - 1) > ((Facemethod == NUMBERFACES) ? 99 :
- X strlen (Facechars)))
- X {
- X printf ("sorry, too many tiles - not enough labels\n");
- X exit (0);
- X }
- X if ((Sizex < 2) || (Sizey < 2))
- X {
- X printf ("sorry, board is too small for a non-trivial game\n");
- X exit (0);
- X }
- X
- X /*
- X * Initialize the curses screen handling functions
- X */
- X#ifndef NOSIGNAL
- X signal (SIGINT, SIG_IGN); /* protect this critical section */
- X#endif
- X initscr ();
- X crmode ();
- X noecho ();
- X#ifdef ARROWKEYS
- X keypad (stdscr, TRUE);
- X#endif
- X#ifndef NOSIGNAL
- X signal (SIGINT, die); /* die() knows how to restore ttymode */
- X#endif
- X /*
- X * From now on use die() to exit the program, otherwise the
- X * ttymode will not be restored.
- X *
- X * Now that curses has been initialized we can check whether the board
- X * fits on the screen.
- X */
- X if (Sizex > (COLS - 1) / 5) /* COLS works only after initscr() */
- X {
- X mvprintw (LINES - 2, 0,
- X "sorry, your screen is not wide enough\n");
- X die ();
- X }
- X if (Sizey > (LINES - 4) / 2)
- X {
- X mvprintw (LINES - 2, 0,
- X "sorry, your screen is not tall enough\n");
- X die ();
- X }
- X
- X game (); /* play the game */
- X
- X mvprintw (LINES - 2, 0,
- X "You've reached the solution in %d moves.\n", Movecnt);
- X die (); /* restore ttymode and exit */
- X}
- X
- Xusage ()
- X{
- X int curtype = UNSPECIFIED;
- X int prevtype = UNSPECIFIED;
- X char *selecttilename = (char *) 0;
- X struct argtablemember *atmp;/* to walk Argopts table */
- X
- X printf ("usage: %s [<width[x<height>]] %s",
- X Myname, "[movemethod] [cursormethod] [facemethod]\n");
- X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
- X {
- X if (atmp -> a_magic == SELECTTILE)
- X selecttilename = atmp -> a_name;
- X if (atmp -> a_type != curtype)
- X {
- X curtype = atmp -> a_type;
- X if (curtype == MOVEMETHOD)
- X printf ("possible movemethods:\n");
- X if (curtype == CURSORMETHOD)
- X printf ("possible cursormethods:\n");
- X if (curtype == FACEMETHOD)
- X printf ("possible facemethods:\n");
- X }
- X switch (atmp -> a_magic)
- X {
- X case KEYS:
- X printf ("\t\t%s <up><down><left><right>", atmp -> a_name);
- X break;
- X case FACES:
- X printf ("\t\t%s <facecharacters>", atmp -> a_name);
- X break;
- X case FACEKEYS:
- X if (selecttilename == (char *) 0)
- X {
- X printf ("aargh: can't find string for SELECTTILE\n");
- X exit (1);
- X }
- X printf ("\t\t%-20.20s (implies movemethod %s)",
- X atmp -> a_name, selecttilename);
- X break;
- X default:
- X printf ("\t\t%-20.20s", atmp -> a_name);
- X break;
- X }
- X if (curtype != prevtype)
- X printf (" (this is default)\n");
- X else
- X printf ("\n");
- X prevtype = curtype;
- X }
- X exit (0);
- X}
- X
- Xconflict (word1, word2)
- Xchar *word1;
- Xchar *word2;
- X{
- X printf ("You may not specify both %s and %s\n", word1, word2);
- X usage ();
- X}
- X
- Xnovalue (word)
- Xchar *word;
- X{
- X printf ("%s requires an argument\n", word);
- X usage ();
- X}
- X
- Xgame () /* initialize board, execute moves */
- X{
- X register int i, j; /* generally used indices and counters */
- X int *board; /* pointer to malloc-ed board */
- X int empty; /* position of empty field */
- X int swap; /* used to swap two tiles */
- X int nswap = 0; /* to determine reachability */
- X int steps; /* number of tiles that move */
- X int unchanged = 0; /* used to indicate that board has changed */
- X int cursorx; /* to save cursorposition while */
- X int cursory; /* printing error messages */
- X int m; /* move, first character / direction */
- X int m2; /* second character of move */
- X /*
- X * Set up the board and shuffle the tiles
- X */
- X board = (int *) malloc ((unsigned) Size2 * sizeof (int));
- X if (board == NULL)
- X {
- X printf ("aargh: malloc failed\n");
- X die ();
- X }
- X srand ((int) time ((long) 0));/* initialize random number generator */
- X for (i = 1; i < Size2; i++) /* put tiles on the board in their */
- X board[i - 1] = i; /* final positions */
- X for (i = Size2 - 2; i > 0; i--)/* permutate tiles */
- X {
- X j = rand () % i;
- X swap = board[i];
- X board[i] = board[j];
- X board[j] = swap;
- X }
- X /*
- X * Check that final position can be reached from current permutation
- X */
- X for (i = 0; i < Size2 - 1; i++)
- X for (j = 0; j < i; j++)
- X if (board[j] > board[i])
- X nswap++;
- X if ((nswap % 2) != 0) /* this position is unreachable */
- X { /* swap two adjacent tiles */
- X swap = board[1];
- X board[1] = board[0];
- X board[0] = swap;
- X }
- X empty = Size2 - 1; /* empty field starts in lower right */
- X board[empty] = BLANK; /* corner */
- X Newpos = empty;
- X Time_start = time ((long *) 0);/* start the clock */
- X
- X while (1) /* until final position is reached */
- X {
- X if (unchanged == 0) /* the board must be (re-)printed */
- X {
- X printboard (board); /* also puts cursor at Newpos */
- X unchanged++; /* the board on the screen is up to date */
- X /*
- X * Check if final position is reached
- X */
- X for (i = 0; i < Size2 - 1; i++)
- X if (board[i] != i + 1)
- X break; /* final position is not yet reached */
- X if (i == Size2 - 1) /* all tiles are in final positions */
- X return; /* game ends */
- X }
- X /*
- X * Let the user make a move
- X */
- X m = getch ();
- X if (m == DIECHAR)
- X die ();
- X if (Movemethod == SELECTTILE)
- X {
- X if (Cursormethod == FACEKEYS)
- X {
- X if (Facemethod == NUMBERFACES)
- X {
- X if (!isdigit (m))
- X {
- X getyx (stdscr, cursory, cursorx);
- X mvprintw (LINES - 1, 0,
- X "use one of the keys");
- X for (i = 0; (i <= Size2) && (i < 10); i++)
- X printw (" %d", i);
- X clrtoeol ();
- X move (cursory, cursorx);
- X refresh ();
- X continue;
- X }
- X if ((m - '0') > (Size2 / 10))
- X m -= '0';
- X else /* we need a second digit */
- X {
- X m = (m - '0') * 10;
- X m2 = getch ();
- X if (m == DIECHAR)
- X die ();
- X if ((!isdigit (m2)) || (m + m2 - '0' >= Size2))
- X {
- X getyx (stdscr, cursory, cursorx);
- X mvprintw (LINES - 1, 0,
- X "use one of the keys");
- X for (i = 0; (i < Size2 % 10) && (i + m < Size2);
- X i++)
- X printw (" %d", i);
- X clrtoeol ();
- X move (cursory, cursorx);
- X refresh ();
- X continue;
- X }
- X m += m2 - '0';
- X }
- X /*
- X * find out where this tile is on the board
- X */
- X for (Newpos = 0; Newpos < Size2; Newpos++)
- X if (board[Newpos] == m)
- X break;/* found tile */
- X if (Newpos == Size2)/* no tile with face m */
- X {
- X mvprintw (LINES - 2, 0,
- X "aargh: can't find tile %d on board\n", m);
- X die ();
- X }
- X }
- X else
- X {
- X /*
- X * Facemethod != NUMBERFACES
- X * This means that a single keystroke identifies the
- X * tile that is to be moved.
- X */
- X for (Newpos = 0; Newpos < Size2; Newpos++)
- X if (board[Newpos] > 0)
- X if (Facechars[board[Newpos] - 1] == m)
- X break;/* found tile */
- X if (Newpos == Size2)
- X {
- X getyx (stdscr, cursory, cursorx);
- X mvprintw (LINES - 1, 0,
- X "use one of the keys ");
- X for (i = 0; (i < Size2 - 1) && (i < 30); i++)
- X printw ("%c", Facechars[i]);
- X if (i < Size2 - 1)
- X printw ("...");
- X clrtoeol ();
- X move (cursory, cursorx);
- X refresh ();
- X continue;
- X }
- X }
- X }
- X else /* Cursormethod != FACEKEYS */
- X {
- X if (m == Up)
- X {
- X if (Newpos >= Sizex)
- X Newpos -= Sizex;
- X unchanged = 0;/* board must be reprinted */
- X continue;
- X }
- X if (m == Down)
- X {
- X if (Newpos + Sizex < Size2)
- X Newpos += Sizex;
- X unchanged = 0;
- X continue;
- X }
- X if (m == Left)
- X {
- X if ((Newpos % Sizex) != 0)
- X Newpos--;
- X unchanged = 0;
- X continue;
- X }
- X if (m == Right)
- X {
- X if (((Newpos + 1) % Sizex) != 0)
- X Newpos++;
- X unchanged = 0;
- X continue;
- X }
- X /*
- X * If a key not in the set { Up, Down, Left, Right } was
- X * typed we fall through and try to move the empty field to
- X * Newpos.
- X */
- X }
- X /*
- X * The user has indicated a new location for the empty field.
- X * The new position of the empty field in the array board is in
- X * Newpos.
- X * We must now check that the new position is on the same row
- X * or the same column as the current position and we must
- X * determine how many tiles must be moved.
- X */
- X if (Newpos == empty)
- X continue; /* nothing changed */
- X steps = 0;
- X if (Newpos > empty)
- X {
- X if ((empty % Sizex + Newpos - empty) < Sizex)
- X {
- X m = Right;
- X steps = Newpos - empty;
- X }
- X else
- X if (((Newpos - empty) % Sizex) == 0)
- X {
- X m = Down;
- X steps = (Newpos - empty) / Sizex;
- X }
- X }
- X else
- X {
- X if (empty % Sizex + Newpos - empty >= 0)
- X {
- X m = Left;
- X steps = empty - Newpos;
- X }
- X else
- X if (((empty - Newpos) % Sizex) == 0)
- X {
- X m = Up;
- X steps = (empty - Newpos) / Sizex;
- X }
- X }
- X if (steps == 0)
- X {
- X getyx (stdscr, cursory, cursorx);
- X mvprintw (LINES - 1, 0,
- X "tile must be in same row as empty field\n");
- X move (cursory, cursorx);
- X refresh ();
- X
- X continue;
- X }
- X }
- X else /* Movemethod is MOVEBLANK or MOVETILE */
- X steps = 1; /* one step per move */
- X /*
- X * m should now be one of the four directions, but it may be an
- X * illegal key. This can not happen if Movemethod == SELECTTILE.
- X *
- X * steps indicates how many tiles are to be moved
- X */
- X if ((m != Up) && (m != Down) && (m != Left) && (m != Right))
- X {
- X getyx (stdscr, cursory, cursorx);
- X#ifdef ARROWKEYS
- X if (Cursormethod == ARROWK)
- X mvprintw (LINES - 1, 0,
- X "Use the arrow keys for up, down, left and right");
- X else
- X#endif
- X if (Movemethod == MOVETILE)
- X {
- X mvprintw (LINES - 1, 0,
- X "use %c for up, %c for down, ", Down, Up);
- X printw ("%c for left and %c for right", Right, Left);
- X }
- X else
- X {
- X mvprintw (LINES - 1, 0,
- X "use %c for up, %c for down, ", Up, Down);
- X printw ("%c for left and %c for right", Left, Right);
- X }
- X clrtoeol ();
- X move (cursory, cursorx);
- X refresh ();
- X continue;
- X }
- X /*
- X * m contains the direction to move
- X * steps contains the number of tiles to move
- X * Apply the move to the board.
- X */
- X if (m == Up)
- X if (empty >= Sizex)
- X while (steps-- > 0)
- X {
- X board[empty] = board[empty - Sizex];
- X board[empty - Sizex] = BLANK;
- X empty -= Sizex;
- X Movecnt++;
- X }
- X if (m == Down)
- X if (empty + Sizex < Size2)
- X while (steps-- > 0)
- X {
- X board[empty] = board[empty + Sizex];
- X board[empty + Sizex] = BLANK;
- X empty += Sizex;
- X Movecnt++;
- X }
- X if (m == Left)
- X if ((empty % Sizex) != 0)
- X while (steps-- > 0)
- X {
- X board[empty] = board[empty - 1];
- X board[empty - 1] = BLANK;
- X empty--;
- X Movecnt++;
- X }
- X if (m == Right)
- X if (((empty + 1) % Sizex) != 0)
- X while (steps-- > 0)
- X {
- X board[empty] = board[empty + 1];
- X board[empty + 1] = BLANK;
- X empty++;
- X Movecnt++;
- X }
- X if (steps == 1) /* you ran into a wall */
- X {
- X getyx (stdscr, cursory, cursorx);
- X mvprintw (LINES - 1, 0,
- X "Your can't cross that wall\n");
- X clrtoeol ();
- X move (cursory, cursorx);
- X refresh ();
- X continue;
- X }
- X if (steps != -1) /* something is very wrong */
- X {
- X mvprintw (LINES - 2, 0,
- X "aargh: couldn't move enough tiles (steps = %d)\n",
- X steps);
- X die ();
- X }
- X Newpos = empty;
- X unchanged = 0; /* the board must be reprinted */
- X }
- X}
- X
- Xprintboard (board)
- Xint *board;
- X{
- X register int i, j;
- X int tilewidth;
- X unsigned long time_used;
- X unsigned minutes;
- X unsigned seconds;
- X unsigned long time_now;
- X
- X tilewidth = ((Facemethod == NUMBERFACES) && (Size2 > 10)) ? 5 : 4;
- X mvprintw ((LINES - 4 - 2 * Sizey) / 2,
- X (COLS - 1 - tilewidth * Sizex) / 2,
- X "+"); /* print top edge of board */
- X for (j = 0; j < Sizex; j++)
- X if (tilewidth == 5)
- X printw ("----+");
- X else
- X printw ("---+");
- X for (i = 0; i < Sizey; i++)
- X {
- X mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 1,
- X (COLS - 1 - tilewidth * Sizex) / 2, "| ");
- X for (j = 0; j < Sizex; j++)
- X if (tilewidth == 5)
- X if (board[Sizex * i + j] != BLANK)
- X if ((Size2 > 9) && (Cursormethod == FACEKEYS) &&
- X (board[Sizex * i + j] <= Size2 / 10))
- X printw ("%02d | ", board[Sizex * i + j]);
- X else
- X printw ("%2d | ", board[Sizex * i + j]);
- X else
- X printw (" | ");
- X else
- X if (board[Sizex * i + j] != BLANK)
- X printw ("%c | ", Facechars[board[Sizex * i + j] - 1]);
- X else
- X printw (" | ");
- X mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 2,
- X (COLS - 1 - tilewidth * Sizex) / 2, "+");
- X for (j = 0; j < Sizex; j++)
- X if (tilewidth == 5)
- X printw ("----+");
- X else
- X printw ("---+");
- X }
- X mvprintw (LINES - 1, 0, "\n");/* erase error messages */
- X /*
- X * Update the clock
- X */
- X time_now = time ((long *) 0);
- X time_used = time_now - Time_start;
- X minutes = time_used / 60;
- X seconds = time_used % 60;
- X mvprintw (LINES - 3, 0, "Time used: %02d min %02d sec Move: %d",
- X minutes, seconds, Movecnt);
- X /*
- X * Put cursor on the position indicated by Newpos
- X */
- X move ((LINES - 4 - 2 * Sizey) / 2 + (Newpos / Sizex) * 2 + 1,
- X (COLS - 1 - tilewidth * Sizex) / 2 +
- X (Newpos % Sizex) * tilewidth + tilewidth / 2);
- X refresh (); /* put all this on the screen */
- X}
- END_OF_FILE
- if test 23213 -ne `wc -c <'puzzle15.c'`; then
- echo shar: \"'puzzle15.c'\" unpacked with wrong size!
- fi
- # end of 'puzzle15.c'
- fi
- echo shar: End of shell archive.
- exit 0
-